// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "media/cast/net/rtp/rtp_sender.h"

#include "base/big_endian.h"
#include "base/logging.h"
#include "base/rand_util.h"
#include "media/cast/constants.h"

namespace media {
namespace cast {

    namespace {

        // If there is only one referecne to the packet then copy the
        // reference and return.
        // Otherwise return a deep copy of the packet.
        PacketRef FastCopyPacket(const PacketRef& packet)
        {
            if (packet->HasOneRef())
                return packet;
            return make_scoped_refptr(new base::RefCountedData<Packet>(packet->data));
        }

    } // namespace

    RtpSender::RtpSender(
        const scoped_refptr<base::SingleThreadTaskRunner>& transport_task_runner,
        PacedSender* const transport)
        : transport_(transport)
        , transport_task_runner_(transport_task_runner)
        , weak_factory_(this)
    {
        // Randomly set sequence number start value.
        config_.sequence_number = base::RandInt(0, 65535);
    }

    RtpSender::~RtpSender() { }

    bool RtpSender::Initialize(const CastTransportRtpConfig& config)
    {
        config_.ssrc = config.ssrc;
        // TODO(xjz): Android TV receivers expect the |payload_type| to be one of
        // these two specific values. This constraint needs to be removed and the
        // value of the |payload_type| can vary according to the spec:
        // https://tools.ietf.org/html/rfc3551.
        if (config.rtp_payload_type <= RtpPayloadType::AUDIO_LAST)
            config_.payload_type = 127;
        else
            config_.payload_type = 96;
        packetizer_.reset(new RtpPacketizer(transport_, &storage_, config_));
        return true;
    }

    void RtpSender::SendFrame(const EncodedFrame& frame)
    {
        DCHECK(packetizer_);
        packetizer_->SendFrameAsPackets(frame);
        LOG_IF(DFATAL, storage_.GetNumberOfStoredFrames() > kMaxUnackedFrames)
            << "Possible bug: Frames are not being actively released from storage.";
    }

    void RtpSender::ResendPackets(
        const MissingFramesAndPacketsMap& missing_frames_and_packets,
        bool cancel_rtx_if_not_in_list, const DedupInfo& dedup_info)
    {
        // Iterate over all frames in the list.
        for (MissingFramesAndPacketsMap::const_iterator it = missing_frames_and_packets.begin();
             it != missing_frames_and_packets.end();
             ++it) {
            SendPacketVector packets_to_resend;
            FrameId frame_id = it->first;
            // Set of packets that the receiver wants us to re-send.
            // If empty, we need to re-send all packets for this frame.
            const PacketIdSet& missing_packet_set = it->second;

            bool resend_all = missing_packet_set.find(kRtcpCastAllPacketsLost) != missing_packet_set.end();
            bool resend_last = missing_packet_set.find(kRtcpCastLastPacket) != missing_packet_set.end();

            const SendPacketVector* stored_packets = storage_.GetFramePackets(frame_id);
            if (!stored_packets)
                continue;

            for (SendPacketVector::const_iterator it = stored_packets->begin();
                 it != stored_packets->end(); ++it) {
                const PacketKey& packet_key = it->first;
                const uint16_t packet_id = packet_key.packet_id;

                // Should we resend the packet?
                bool resend = resend_all;

                // Should we resend it because it's in the missing_packet_set?
                if (!resend && missing_packet_set.find(packet_id) != missing_packet_set.end()) {
                    resend = true;
                }

                // If we were asked to resend the last packet, check if it's the
                // last packet.
                if (!resend && resend_last && (it + 1) == stored_packets->end()) {
                    resend = true;
                }

                if (resend) {
                    // Resend packet to the network.
                    VLOG(3) << "Resend " << frame_id << ":" << packet_id;
                    // Set a unique incremental sequence number for every packet.
                    PacketRef packet_copy = FastCopyPacket(it->second);
                    UpdateSequenceNumber(&packet_copy->data);
                    packets_to_resend.push_back(std::make_pair(packet_key, packet_copy));
                } else if (cancel_rtx_if_not_in_list) {
                    transport_->CancelSendingPacket(it->first);
                }
            }
            transport_->ResendPackets(packets_to_resend, dedup_info);
        }
    }

    void RtpSender::CancelSendingFrames(const std::vector<FrameId>& frame_ids)
    {
        for (FrameId i : frame_ids) {
            const SendPacketVector* stored_packets = storage_.GetFramePackets(i);
            if (!stored_packets)
                continue;
            for (SendPacketVector::const_iterator j = stored_packets->begin();
                 j != stored_packets->end(); ++j) {
                transport_->CancelSendingPacket(j->first);
            }
            storage_.ReleaseFrame(i);
        }
    }

    void RtpSender::ResendFrameForKickstart(FrameId frame_id,
        base::TimeDelta dedupe_window)
    {
        // Send the last packet of the encoded frame to kick start
        // retransmission. This gives enough information to the receiver what
        // packets and frames are missing.
        MissingFramesAndPacketsMap missing_frames_and_packets;
        PacketIdSet missing;
        missing.insert(kRtcpCastLastPacket);
        missing_frames_and_packets.insert(std::make_pair(frame_id, missing));

        // Sending this extra packet is to kick-start the session. There is
        // no need to optimize re-transmission for this case.
        DedupInfo dedup_info;
        dedup_info.resend_interval = dedupe_window;
        ResendPackets(missing_frames_and_packets, false, dedup_info);
    }

    void RtpSender::UpdateSequenceNumber(Packet* packet)
    {
        // TODO(miu): This is an abstraction violation.  This needs to be a part of
        // the overall packet (de)serialization consolidation.
        static const int kByteOffsetToSequenceNumber = 2;
        base::BigEndianWriter big_endian_writer(
            reinterpret_cast<char*>((&packet->front()) + kByteOffsetToSequenceNumber),
            sizeof(uint16_t));
        big_endian_writer.WriteU16(packetizer_->NextSequenceNumber());
    }

    int64_t RtpSender::GetLastByteSentForFrame(FrameId frame_id)
    {
        const SendPacketVector* stored_packets = storage_.GetFramePackets(frame_id);
        if (!stored_packets)
            return 0;
        PacketKey last_packet_key = stored_packets->rbegin()->first;
        return transport_->GetLastByteSentForPacket(last_packet_key);
    }

} //  namespace cast
} // namespace media
